import numpy as np
from pandas import DataFrame, Series
import pandas as pd

### 一、pandas的数据结构

## 1 Series
# 1.1 Series的创建：
# 1.1.1 由列表
l = [1, 2, 3, 4]
s = Series(l)
print(s)
print(s.index)
print(s.values, type(s.values))
s.index = list('abcd')
print(s.index)
s['a'] = 'a'
print(s)
print(l)
# 1.1.2 由numpy数组
n1 = np.arange(4)
print(n1.dtype)
s1 = Series(n1)
print(s1)
s1[0] = 8.4  # 不能对不是同类型的进行操作
print(s1)
print(n1)
print('-' * 7)
n2 = np.arange(4.0)
print(n2.dtype)
s2 = Series(n2)
print(s2)
s2[0] = 8.4  # 不能对不是同类型的进行操作
print(s2)
print(n2)
# 1.1.3 由字典创建
# 字典的key对应Series的索引
s = Series({'a': 1, 'b': 2, 'c': 3, 'd': 4})
print(s)

# 1.2 Series的索引和切片
# 1.2.1 显式索引，在显式索引前有隐式索引看不见0.1.2.3...
# 索引
print(s['a'])  # 不推荐
print(s.loc['a'])  # 推荐
print(s.loc[['a', 'c']])
# 切片
print(s['a':'c'])
print(s.loc['a':'c'])  # 全封闭
# 1.2.2 隐式索引
# 索引
print(s[0])  # 不推荐，会出错
print(s.iloc[0])  # 推荐
# 切片
print(s[0:3])
print(s.iloc[0:3])  # 左闭右开

# 1.3 Seriess的基本概念
print(s.shape)
print(s.size)
print(s.index)
print(s.values)
print(s.head(3))
print(s.tail(3))
print(pd.isnull(s))
print(s.isnull())
print(pd.notnull(s))
print(s.notnull())
s.name = 'kfx'
print(s.name)

# 1.4 Series的运算
# 1.4.1 适用于numpy的数组运算也适用于Series
print(s + 1)
# 1.4.2 Series之间的运算
s1 = Series([1, 2, 3, 4])
s2 = Series(np.random.randint(1, 10, size=4), index=np.arange(2, 6))
print(s1)
print(s1 + s2)  # 数据会丢失
# 使用pandas封装的运算函数，保留所有index的value
# + add()
print(s1.add(s2, fill_value=100))
# - sub() subtract()
print(s1.sub(s2, fill_value=100))
# * mul() multiply()
print(s1.mul(s2, fill_value=100))
# / truediv() div() divide()
print(s1.truediv(s2, fill_value=100))
# // floordiv()
print(s1.floordiv(s2, fill_value=100))
# % mod()
print(s1.mod(s2, fill_value=100))
# ** pow()
print(s1.pow(s2, fill_value=100))

## 2. DataFrame
# 2.1 DataFrame创建
index = ['张三', '李四', '王五', '赵六']
columns = ['语文', '数学']
# 2.1.1 字典创建
df0 = DataFrame({'语文': np.random.randint(0, 100, size=4),
                 '数学': np.random.randint(0, 100, size=4)},
                index=index, columns=['英语', '数学'])
print(df0)
# 2.1.2 分块创建
data = np.random.randint(0, 100, size=(4, 2))
df = DataFrame(data=data, index=index, columns=columns)
print(df)

# 2.1.3 属性
print(df.index)
print(df.columns)
print(df.values)
print(df.shape)

# 2.2 DataFrame索引切片
# 2.2.1 对列
# 索引
print(df['语文'])
print(df.语文)
# 赋值
df['理综'] = np.random.randint(0, 100, size=(4,))
print(df)
# 切片
print(df[['数学']])
print(df.iloc[:, 1:2])
# 2.2.2 对行
# 索引
print(df.loc['张三'])
print(df.iloc[0])
# 切片
print(df['张三':'李四'])  # 全闭区间
# 2.2.3 对元素进行索引的方法
# 先列后行
print(df['数学']['李四'])
# 先行后列
print(df.loc['李四']['数学'])
# 以上两种属于链式索引，会引发错误，推荐写法
print(df.loc['李四', '数学'])

# 2.3 DataFrame的运算
# DataFrame和DataFrame之间的运算，行列索引全部一致才运算，不一致补NaN
print(df0 + df)
# 填充NaN
print(df.add(df0, fill_value=0))
# 与Series计算
s = Series(data=np.random.randint(1, 100, size=(4)),
           index=['数学', '理综', '英语', '语文'])  # 直接进行运算，观察DataFrame的列索引和Series的索引是否一致，一致就进行相应的运算
print(df + s)
s1 = Series(data=np.random.randint(1, 100, size=(4)),
            index=['张三', '李四', '王五', '赵六'])
print(df + s1)  # 使用运算符的默认操作是DataFrame的列索引和Series的索引进行比较
print(df.add(s1, axis=0))  # 使用axis改变运算的方向
